import MeiTuan
from MeiTuan.outlier_detection import anormal_region_detection
from MeiTuan.dispatch import two_stage_fast_heuristic, highQ_two_stage_fast_heuristic
from MeiTuan.cal_utility import calUtility
from MeiTuan.generateOrders import fitting_dist, orders_list, restaurant_list, home_list
from env import *
from order import Order
from mpi4py import MPI
import asyncio
import random
from merchant import Merchant
from user import User
import matplotlib.pyplot as plt
import csv
from regulation.dispatch.base_dispatch import *
from regulation.reposition.base_reposition import *
from agentDesign.L1.read_map import AStarPlanner, ReadMap
from repast4py.space import DiscretePoint as dpt


class Platform:

    def __init__(self, order_prob: list, order_BF: int, order_per: float, model):
        """
            order_prob: 按照商区划分，划分不同区域的订单生成的概率
            order_BF: 订单波峰设置
        """
        self.order_BF = order_BF
        self.order_prob = order_prob
        self.region_info = ["A", "B", "C", "D", "E"]  # 普通订单生成方式按照不同区域会有数量差异
        self.order_id = [0]  # 记录生成的所有的订单数目
        self.normal_orders = [[] for _ in range(NUM_REGION)]  # 存储每个区域普通订单信息
        self.normal_orders_dict = {}
        self.high_quality_orders = [[] for _ in range(NUM_REGION)]  # 存储每个区域的高质量订单
        self.previous_high_quality_orders = [[] for _ in range(NUM_REGION)]  # 存储上一个step每个区域的高质量订单
        self.highQ_orders_dict = {}
        self.order_per = order_per  # 高质量订单和普通订单的比例
        self.model = model  # 模型
        self.solution = {}  # 当前时间实现的派单结果存储
        self.order_num = 0  # runner_step每一次的订单数目
        self.users = {}
        self.merchants = {}
        self.init_agents()
        self.money_ranking = []
        self.money_allowed = 0
        self.prev_outliers = None


        self.regulation_data = dict(algorithm=[], idx=[], name=[], value=[], params=[]) # 存储调控设置

        self.dispatch_mode = "None" # 默认派单方法
        self.dispatch_params = dict(idx=list(self.model.riders_list))

        self.reposition_mode = "use outlier"  # 默认重调度方法
        self.reposition_params = dict(idx=list(self.model.riders_list))

        # 异常检测的模型
        self.model_int = None
        self.model_r = None
        

    def init_agents(self):
        i = 0
        for points in home_list:  # 用户
            for point in points:
                user = User(dpt(point[0], point[1], 0), i, self.model.rank)
                i += 1
                self.users[i] = user
                self.model.context.add(user)
                self.model.grid.move(user, user.location)

        i = 0
        for points in restaurant_list:  # 商家
            for point in points:
                merchant = Merchant(dpt(point[0], point[1], 0), i, self.model.rank, 10)
                i += 1
                self.merchants[i] = merchant
                self.model.context.add(merchant)
                self.model.grid.move(merchant, merchant.pt)

    def handle_regulation(self):
        """
        处理传送过来的调控信息并进行调控

        """
        removes = []
        for i in range(len(self.regulation_data["algorithm"])):
            # 调控派单策略
            if self.regulation_data["algorithm"][i] == "dispatch algorithm":
                if "start" in self.regulation_data["params"][i] and \
                        int(self.regulation_data["params"][i]["start"]) <= self.model.runner_step and int(
                    self.regulation_data["params"][i]["end"]) >= self.runner_step:
                    self.dispatch_mode = self.regulation_data["value"][i]
                    self.dispatch_params = self.regulation_data["params"][i]
                elif "start" not in self.regulation_data["params"][i]:
                    self.dispatch_mode = self.regulation_data["value"][i]
                    self.dispatch_params = dict(idx=list(self.riders_list.keys()))
                else:
                    self.dispatch_mode = "TSFH"
                    self.dispatch_params = dict(idx=list(self.riders_list.keys()))
                    removes.append(i)

            if self.regulation_data["algorithm"][i] == "reposition algorithm":
                if "start" in self.regulation_data["params"][i] and \
                        int(self.regulation_data["params"][i]["start"]) <= self.model.runner_step and int(
                    self.regulation_data["params"][i]["end"]) >= self.runner_step:
                    self.reposition_mode = self.regulation_data["value"][i]
                    self.reposition_params = self.regulation_data["params"][i]
                elif "start" not in self.regulation_data["params"][i]:
                    self.reposition_mode = self.regulation_data["value"][i]
                    self.reposition_params = dict(idx=list(self.riders_list.keys()))
                else:
                    self.reposition_mode = "None"
                    self.reposition_params = dict(idx=list(self.riders_list.keys()))
                    removes.append(i)

        for i in removes:
            del self.regulation_data["algorithm"][i], self.regulation_data["name"][i], self.regulation_data["value"][i], \
            self.regulation_data["params"][i] 

    def reposition(self, repo_observ):
        """骑手重调度

        Args:
            repo_observ (_type_): dict
        """
        reposition_action = []
       
        repo_observ["rider_id"] = self.reposition_params["idx"]

        print("num rider: {} !!!!!!!!!!!!".format(len(repo_observ["rider_id"])))
        self.model.is_reposition  = 0 # 重调度初始化为0
        self.model.is_anomalies = 0 #异常信息初始为0
        if self.reposition_mode == "random":
            reposition_action = random_reposition(repo_observ, read_map.get_door("道路"))
        elif self.reposition_mode == "use outlier":
            # region_obser = self.model.generate_region_info()
            rider_infos = []
            for rid in repo_observ["rider_id"]:              
              agent = self.model.context.agent((rid, 0, self.model.rank))
              rider_infos.append({
                "id": agent.id,
                "status": agent.status,
                "work_region": agent.work_region
              })
            repo_observ["rider_infos"] = rider_infos
            # outlier, self.model_r, self.model_int = anormal_region_detection(region_obser, self.model_r, self.model_int)
            reposition_action, self.model_r, self.model_int, self.model.is_anomalies, self.model.outlier_info = useOutlier(repo_observ,  self.model_r, self.model_int, read_map.get_door("道路"))
        #print("重调度动作:", reposition_action)
        if len(reposition_action): self.model.is_reposition = 1 # 有重调度的动作
        for rep in reposition_action:
            agent = self.model.context.agent((rep["rider_id"], 0, self.model.rank))
            if agent.order_count == 0 and agent.status != 0 and agent.status != 1: #只有当没有单的时候才进行重调度
                agent.status = 3
                agent.work_region = get_which_region([rep["destination"][0],
                                                   rep["destination"][1]])
                agent.position = []
                a_star_walk = AStarPlanner(agent.location[0], agent.location[1], rep["destination"][0],
                                                   rep["destination"][1])
                walks = a_star_walk.planning()
                # agent.position += a_star_walk.planning()

                for i in range(len(walks[0])):
                    agent.position.append([walks[0][i], walks[1][i]])
                with open('data.csv', 'a', newline='') as file:
                    writer = csv.writer(file)
                    writer.writerow([self.model.runner_step, agent.id, len(agent.position), agent.work_region])
                print(self.model.runner_step, agent.id, '*'*10, len(agent.position))
    def reposition_without(self, repo_observ):
        """只进行异常检测，不进行重调度

        Args:
            repo_observ (_type_): dict
        """
        repo_observ["rider_id"] = self.reposition_params["idx"]

        self.model.is_anomalies = 0 #异常信息初始为0

        if self.reposition_mode == "use outlier":
            rider_infos = []
            for rid in repo_observ["rider_id"]:              
                agent = self.model.context.agent((rid, 0, self.model.rank))
                rider_infos.append({
                    "id": agent.id,
                    "status": agent.status,
                    "work_region": agent.work_region
                })
            repo_observ["rider_infos"] = rider_infos
            
            # 只进行异常检测 
            outliers, self.model_r, self.model_int = useOutlier_without(repo_observ, self.model_r, self.model_int)
            self.model.outlier_info = outliers[-1]
            # 更新 prev_outliers
            if self.prev_outliers is not None and np.array_equal(outliers, self.prev_outliers):
                print("No new anomalies detected.")
            else:
                print("New anomalies detected.")
                self.model.is_anomalies = 1
                self.prev_outliers = outliers
        
    # def regulation_dispatch(self):
    #     # self.dispatch_n
    #     dispatch_observ = self.model.generate_dispatch_info(self.dispatch_params["idx"])
        # if self.dispatch_mode == "TSFH":
        #     for i in range(len(self.meituan.normal_orders)):
        #         if self.riders[i]: random.shuffle(self.riders[i])
        #         solution.update(two_stage_fast_heuristic(self.meituan.normal_orders[i], self.riders[i], D=100))

        #     return solution

            # 这些调度方法和上面返回的格式不一样，记得修改
        # if self.dispatch_mode == "greedy":
        #     dispatch_action = GreedyAlgorithm(dispatch_observ)
        # elif self.dispatch_mode == "distance_greedy":
        #     dispatch_action = DistanceGreedyAlgorithm(dispatch_observ)
        # elif self.dispatch_mode == "NNP":
        #     dispatch_action = NearestNeighborPriority(dispatch_observ)
        # elif self.dispatch_mode == "Hungarian":
        #     dispatch_action = HungarianAlgorithm(dispatch_observ)
        
        # print(dispatch_action)



    def normal_dispatch(self):
        """ 派单

        Args:
            riders (_type_):骑手class
        """
        solution = {}
        route_length = {}
        for i in range(len(self.normal_orders)):  # 只派单普通订单
            for rider in self.model.riders[i]:
                route_length[rider.id] = len(rider.route)
            if self.model.riders[i]:
                random.shuffle(self.model.riders[i])
            solution.update(two_stage_fast_heuristic(self.normal_orders[i], self.model.riders[i],
                                                     self.previous_high_quality_orders[i], D=100, current_time = self.model.runner_step))
        # print('normal派单算法加载完毕')
        # print('solution', solution)
        self.solution = solution

        for agent in self.model.context.agents():
            # 将旗手的送单信息与旗手的送单坐标整合在一起
            if agent.TYPE == 0 and agent.id in solution:
                for i in range(len(solution[agent.id])):
                    if i == 0: continue
                    order_id = agent.route[route_length[agent.id]+i - 1][1]
                    # print(agent.route)
                    # print(route_length[agent.id]+i - 1)
                    # print(agent.route[route_length[agent.id]+i - 1])
                    # print(agent.route[-1][1])
                    # print(order_id)
                    region_order = next(
                        (order for order_line in self.normal_orders for order in order_line if order.id == order_id),
                        None)
                    # print("222", region_order)
                    # print("111", solution[agent.id][i])
                    agent.route[route_length[agent.id]+i - 1] = agent.route[route_length[agent.id]+i - 1] + solution[agent.id][i] + (
                        region_order.region, region_order.money)  # 将订单的信息（区域和金额）添加到骑手的路线中。
                   #  print('agent.route', agent.route)

    def highQ_dispatch(self):
        """ 派单

        Args:
            riders (_type_):骑手class
        """
        solution = {}
        for i in range(len(self.high_quality_orders)):  # 只派单普通订单
            if self.model.riders[i]:
                random.shuffle(self.model.riders[i])
            solution.update(highQ_two_stage_fast_heuristic(self.high_quality_orders[i], self.model.riders[i], D=100,  current_time = self.model.runner_step))
        # print('highQ派单算法加载完毕')
        # print('solution', solution)
        self.solution = solution

        for agent in self.model.context.agents():
            # 将旗手的送单信息与旗手的送单坐标整合在一起
            if agent.TYPE == 0 and agent.id in solution:
                for i in range(len(solution[agent.id])+1):
                    if i == 0: continue
                    order_id = agent.route[i - 1][1]
                    region_order = next(
                        (order for order_line in self.high_quality_orders for order in order_line if
                         order.id == order_id),
                        None)
                    agent.route[i - 1] = agent.route[i - 1] + solution[agent.id][i-1] + (
                        region_order.region, region_order.money)  # 将订单的信息（区域和金额）添加到骑手的路线中。
                    # print('agent.route', agent.route)

    def generate_order_num_step(self, runner_step):
        """ 生成每个时间点生成订单的信息，再调用两个平台生成订单的方式

        Args:
            runner_step (_type_): 当前时间点
        """
        test_i = runner_step % ONE_DAY
        order_type = {"A": ((test_i - 20) % 80) * self.order_BF,
                      "B": ((test_i - 40) % 80) * self.order_BF,
                      "C": ((test_i - 60) % 80) * self.order_BF,
                      "D": ((test_i - 50) % 100) * self.order_BF,
                      "E": ((test_i) % 90) * self.order_BF}  # 时间点对应的不同区域生成订单信息
        for type_order in self.region_info:
            prob_order = self.order_prob[type_order]
            order_num = int(prob_order * int(fitting_dist(order_type[type_order])))  # 生成当前区域生成订单的数量
            self.order_num = order_num
            high_quality_order_num = int(order_num * self.order_per)
            high_quality_orders = orders_list(high_quality_order_num, runner_step, type_order,
                                              high_quality=True)  # 生成高质量订单信息
            normal_order_num = order_num - high_quality_order_num
            normal_orders = orders_list(normal_order_num, runner_step, type_order)  # 生成普通订单信息
            self.generate_normal_order(normal_orders, runner_step)
            self.generate_high_quality_order(high_quality_orders, runner_step)

    def generate_normal_order(self, normal_orders, runner_step):
        """
            普通订单生成平台调度
        """
        for order in normal_orders:
            user = self.model.grid.get_agent(dpt(order[3][0], order[3][1], 0))
            # print('user', user)
            merchant = self.model.grid.get_agent(dpt(order[2][0], order[2][1], 0))
            # print('merchant', dpt(order[2][0], order[2][1], 0), merchant)
            new_order = Order(self.order_id[0], order[2], order[3],
                              (0, 60), (0, 60), order[1], runner_step, False, merchant.id, user.id)
            new_order.region = order[0]  # 订单区域编号
            self.order_id[0] += 1
            self.normal_orders[order[0]].append(new_order)
            self.normal_orders_dict[new_order.id] = new_order
        # print("putong dingdan fa bu")
        # print(self.normal_orders)
    
    def generate_high_quality_order(self, high_quality_orders, runner_step):
        for order in high_quality_orders:
            user = self.model.grid.get_agent(dpt(order[3][0], order[3][1], 0))
            merchant = self.model.grid.get_agent(dpt(order[2][0], order[2][1], 0))
            new_order = Order(self.order_id[0], order[2], order[3],
                              (0, 60), (0, 60), order[1], runner_step, True, merchant.id, user.id)
            new_order.region = order[0]
            self.order_id[0] += 1
            self.high_quality_orders[order[0]].append(new_order)  # 存储高质量订单
            self.highQ_orders_dict[new_order.id] = new_order
            # print(new_order.id)
        # print("高质量订单发布")
        # print(self.high_quality_orders)

    def normal_order_update(self, runner_step):
        # 普通订单
        for i in range(len(self.normal_orders)):
            for order in self.normal_orders[i]:
                if int(runner_step) >= int(order.delete_time) and order.status == "unprocessed":
                    order.status = 'processed'
                    self.normal_orders[i].remove(order)
                    del self.normal_orders_dict[order.id]
                elif order.status == "processed":
                    self.normal_orders[i].remove(order)
                    del self.normal_orders_dict[order.id]

    def highQ_order_update(self, runner_step):
        # 高质量订单
        for i in range(len(self.high_quality_orders)):
            for order in self.high_quality_orders[i]:
                if int(runner_step) >= int(order.delete_time) and order.status == "unprocessed":
                    order.status = 'processed'
                    self.high_quality_orders[i].remove(order)
                    del self.highQ_orders_dict[order.id]
                elif order.status == "processed":
                    self.high_quality_orders[i].remove(order)
                    del self.highQ_orders_dict[order.id]

    def refresh_order(self):
        for new_id, order in enumerate(self.normal_orders, start=1):
            order.id = new_id
            if order.status == "unprocessed":
                self.model.region_info_update_form_order(order)

    def money_ranked(self, riders_all):
        money_r = []
        for rider in riders_all:
            if rider.money > 0 and self.money_allowed == 0:
                self.money_allowed = 1
            money_r.append((rider.id, rider.money))
        self.money_ranking = sorted(money_r, key=lambda x: x[1], reverse=True)

    def get_money_rank(self, rider_id):
        for i in range(len(self.money_ranking)):
            if rider_id == self.money_ranking[i][0]:
                return i+1

    def step(self, runner_step):
        self.highQ_order_update(runner_step)
        # for i in range(len(self.high_quality_orders)):
        #     for j in range(len(self.high_quality_orders[i])):
        #         print(self.high_quality_orders[i][j].id)
        # print("111", self.high_quality_orders)
        # 高质量订单发布1个step后对其进行分配（按照好评率和距离）
        self.highQ_dispatch()
        self.previous_high_quality_orders = self.high_quality_orders
        # print("222", self.previous_high_quality_orders)
        # 更新订单序列状态
        self.normal_order_update(runner_step)
        # 生成新订单
        self.generate_order_num_step(runner_step)
        # 普通订单发布后直接进行分配（按照业绩和距离）
        self.normal_dispatch()
        self.handle_regulation()



async def run_model(model, meituan):
    for i in range(10):
        await model.step()
        meituan.step(i)
        print('info', meituan.normal_orders, meituan.high_quality_orders)


# if __name__ == "__main__":
#     order_prob = {
#         "A": 0.008,
#         "B": 0.008,
#         "C": 0.03,
#         "D": 0.03,
#         "E": 0.03
#     }
#     riders = [[] for _ in range(NUM_REGION)]

#     model = Model(0, MPI.COMM_WORLD, None, 5, [200, 200, 200, 200, 200],
#                   [2, 2, 2, 3, 4], 3, 0.5, [0, 0, 0, 0, 3],
#                   [1, 1, 1, 1, 1], [10, 10, 10, 10, 10])

#     meituan = MeiTuan(order_prob, 3, 0.2, model)
#     asyncio.run(run_model(model, meituan))




